package com.cloud.lock.redis;

import com.cloud.lock.redis.LockContext;
import com.cloud.lock.redis.Locker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Component
public class RedisLocker implements Locker {

    private String LUA_LOCK_SCRIPT = "return redis.call('SET', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2]) ";

    private RedisScript<String> lockScript = new DefaultRedisScript<String>(LUA_LOCK_SCRIPT, String.class);

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public LockContext getKey(String key) {

        String lockKey = "lock:" + key;
        String value = UUID.randomUUID().toString();

        String result = stringRedisTemplate.execute(lockScript, stringRedisTemplate.getStringSerializer(),
                stringRedisTemplate.getStringSerializer(),
                Collections.singletonList(lockKey),
                new Object[]{value, 1000 + ""});

        if (result != null && result.equalsIgnoreCase("ok")) {
            LockContext lockContext = new LockContext();
            lockContext.setKey(lockKey);
            lockContext.setValue(value);

            return lockContext;
        } else {
            return null;
        }
    }

    @Override
    public void releaseLock(LockContext lockContext) {
        releaseLock(lockContext, 0L);
    }

    @Override
    public void releaseLock(LockContext lockContext, Long expireTime) {
        //校验 是否同一个个锁
        if (lockContext == null || lockContext.getKey() == null
                || lockContext.getValue() == null) {
            return;
        }
        String value = stringRedisTemplate.opsForValue().get(lockContext.getKey());
        if (!value.equalsIgnoreCase(lockContext.getValue())) {
            //不是同一个锁
            return;
        }
        if (expireTime > 0) {
            stringRedisTemplate.expire(lockContext.getKey(), expireTime, TimeUnit.SECONDS);
        } else {
            stringRedisTemplate.delete(lockContext.getKey());
        }
    }
}
